/**
 * @file readwrite_lock.hpp
 * @date 2021/10/28 16:10
 *
 * @author harrybao (harrybao@123u.com)
 *
 * @brief 读写锁实现
 *
 * @version v1.0.0
 *
 * @copyright Copyright © 2021 harrybao All Rights Reserved
 *
 * @par history:
 * <table>
 * <tr><th>Date                     <th>Version     <th>Author <th>Description
 * <tr><td>2021/10/28 16:10         <td>1.0.0       <td>harrybao
 * <td>创建初始版本
 * </table>
 */

#include <condition_variable>
#include <mutex>

#ifdef DEBUG
#include <cassert>
#include <iostream>
#endif  // DEBUG

namespace hthread {

/**
 * @brief 读写锁
 *
 */
class readwrite_lock {
 public:
  readwrite_lock() : m_rcount(0), m_wcount(0), m_writing(false) {}
  ~readwrite_lock() = default;

 public:
  inline std::size_t rcount() const { return m_rcount; }
  inline std::size_t wcount() const { return m_wcount; }
  inline bool writing() const { return m_writing; }

 public:
  /**
   * @brief 读加锁
   *
   */
  void rlock() {
    {
      std::unique_lock<std::mutex> lock(m_mtx);
      m_rcond.wait(lock, [this]() -> bool {
#ifdef DEBUG
        std::cout << ">>> "
                  << " reading rc:" << this->m_rcount
                  << " wc:" << this->m_wcount << '\n';
#endif  // DEBUG
        return this->m_wcount == 0;
      });

      ++m_rcount;
    }
  }

  /**
   * @brief 读加锁
   *
   */
  void runlock() {
    {
      std::unique_lock<std::mutex> lock(m_mtx);
#ifdef DEBUG
      std::cout << "runlock rc:" << m_rcount << " wc:" << m_wcount << '\n';
      assert(m_rcount > 0);
#endif  // DEBUG
      --m_rcount;
    }
    if (m_rcount == 0 && m_wcount > 0) {
      m_wcond.notify_one();  // r -> w
    }
  }

  /**
   * @brief 写加锁
   *
   */
  void wlock() {
    {
      std::unique_lock<std::mutex> lock(m_mtx);
      ++m_wcount;
      m_wcond.wait(lock, [this]() -> bool {
#ifdef DEBUG
        std::cout << "<<< write wait rc:" << this->m_rcount
                  << " wc:" << this->m_wcount << '\n';
#endif  // DEBUG
        return this->m_rcount == 0 && !this->m_writing;
      });
      m_writing = true;
    }
  }

  /**
   * @brief 写解锁
   *
   */
  void wunlock() {
    {
      std::unique_lock<std::mutex> lock(m_mtx);
      m_writing = false;

#ifdef DEBUG
      std::cout << "wunlock rc:" << m_rcount << " wc:" << m_wcount << '\n';
      assert(m_wcount > 0);
#endif  // DEBUG

      --m_wcount;
    }
    if (m_wcount > 0) {
      m_wcond.notify_one();  // w -> w
    } else {
      m_rcond.notify_all();  // w -> r
    }
  }

 private:
  /// 互斥变量
  std::mutex m_mtx;
  /// 读条件变量
  std::condition_variable m_rcond;
  /// 写条件变量
  std::condition_variable m_wcond;
  /// 读数量
  std::size_t m_rcount;
  /// 写数量
  std::size_t m_wcount;
  /// 正在读
  bool m_writing;
};

}  // namespace hthread